定位程序崩溃点的方法探究

context

昨天遇到问题,当我的 NavitgationController back 的时候,程序崩溃,诡异的地方是: 控制台完全没有任何打印,崩溃的断点直接回到了 main 函数中.

几个尝试

想到之前曾经读过一篇出现在这样情景崩溃的文章:有对象依赖被 pop 的控制器,结果导致了崩溃,但是经过仔细排查,发现并没有这样的情况.

查找资料

网上对崩溃问题的定位常规方法总结起来有:

1.添加通用断点

  1. 选择 BreakPoint Navigator,点击右下角的 ‘+’ ,然后在弹窗中选择 ‘Add Exception BreakPoint’

  1. 右键断点,选择 ‘Edit BreakPoint’,检查设置如下

然后运行程序,程序就能定位到出现崩溃的代码.

但是很遗憾的是,这种方式只能解决大部分的问题(比如不识别的selector 等),有很多类型的崩溃它是不能定位的

比如出现 EXEC_BAD_ACCESS这种错误,以上的方法是不能定位的.

2.重写object的respondsToSelector方法

1.重写object的respondsToSelector方法,现实出现EXEC_BAD_ACCESS前访问的最后一个object.因为有时候程序崩溃根本不知错误发生在什么地方。

在可能出现问题的 .m 或者.mm 文件中加入以下代码

1
2
3
4
5
6
#ifdef _FOR_DEBUG_  
-(BOOL) respondsToSelector:(SEL)aSelector {
printf("SELECTOR: %s\n", [NSStringFromSelector(aSelector) UTF8String]);
return [super respondsToSelector:aSelector];
}
#endif
  1. other c flags中加入-D _FOR_DEBUG_(记住请只在Debug Configuration下加入此标记),这样当你程序崩溃时,Xcode的console上就会准确地记录了最后运行的object的方法。


很不幸,这样还是没有定位我出现问题的代码.

最终方案

首先说一下 EXC_BAD_ACCESS 这个错误,可以这么说,90%的错误来源在于对一个已经释放的对象进行release操作.那么我们应该启用 僵尸对象.方法如下:

Product->Scheme->Edit Scheme->Arguments 的 Environment Variables 中,增加标计位NSZombieEnabled设为YES)objc


这样,就能看到崩溃的具体原因了.但是如果想知道代码,这个原文说需要借助 Xcode 控制台的 GDB,很不幸的是,高版本的 Xcode 中,已经没有切换到 GDB 的功能了.网上也有帖子介绍 GDB 对应 lldb 的指令是什么,很不幸,都不能正常工作,所以这里不在罗列.

后来,我想到是否可以借助终端来完成任务?

那么问题来了,终端怎么知道这个内存地址是属于谁的?
后来想到,可以通过活动监视器,拿到我们程序进程的 pid

我的是 1175

然后参考刚刚原文中的指令, sudo malloc_history 1175 0x7a692620
后面一个参数是崩溃的地址.

此时终端提示,没有打印 MallocStackLogging .

其实这里是需要在 Xocde 中配置的.按照刚刚配置 NSZombieEnabled的位置和方式,添加一个参数:

MallocStackLoggingNoCompact 值设置为 YES

然后继续重新编译运行,查看 pid ,查看崩溃的内存地址.

见到终端打印出了调用顺序,一般来说,是最后一个你自定义的方法导致的崩溃.

然后就是解决 bug 了,祝好运 ~

参考网址:
http://blog.csdn.net/totogo2010/article/details/8949440
http://mobile.51cto.com/iphone-279455.htm